﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;
using System.Runtime.Intrinsics.X86;

namespace PowerHub.MES.Jwt
{
    /// <summary>
    /// 自定义授权过滤器，用于重写 OnAuthorizationAsync 方法并返回自定义数据消息。
    /// </summary>
    public class MyAuthorizeFilter : IAsyncAuthorizationFilter
    {
        private JwtService _jwtService { get; set; }
        /// <summary>
        ///
        /// </summary>
        /// <param name="jwtService"></param>
        public MyAuthorizeFilter(JwtService jwtService)
        {
            _jwtService = jwtService;
        }

        /// <summary>
        /// 检查授权过滤器中是否存在 AllowAnonymous 特性，用于判断是否允许匿名访问。
        /// </summary>
        /// <param name="context">授权过滤器的上下文，包含了授权相关的信息。</param>
        /// <returns>如果存在 AllowAnonymous 特性则返回 true，否则返回 false。</returns>
        private static bool HasAllowAnonymous(AuthorizationFilterContext context)
        {
            var filters = context.Filters;
            for (var i = 0; i < filters.Count; i++)
            {
                if (filters[i] is IAllowAnonymousFilter)
                {
                    return true;
                }
            }

            // 当使用端点路由时，MVC 不会为在控制器和操作中发现的 AllowAnonymous 特性添加 AllowAnonymousFilters。
            // 为了与 2.x 版本兼容，我们将检查端点元数据中是否存在 IAllowAnonymous。
            var endpoint = context.HttpContext.GetEndpoint();
            if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        ///过滤token
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (!context.IsEffectivePolicy(this))
            {
                return;
            }
            // 判断是不添加了[AllowAnonymous]特性，是则跳过身份验证，反之验证
            if (HasAllowAnonymous(context))
            {
                return;
            }
            else
            {

                //获取请求头Token值
                var token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()
                   ?? context.HttpContext.Request.Headers["X-Token"].FirstOrDefault()
                   ?? context.HttpContext.Request.Query["Token"].FirstOrDefault()
                   ?? context.HttpContext.Request.Cookies["Token"];

                ClaimsPrincipal user = context.HttpContext.User;
                try
                {
                    if (_jwtService.ValidatedJwtToken(token, out user))
                    {
                        //验证通过，验证成功  赋值给User
                        context.HttpContext.User = user;
                    }
                    else
                    {
                        context.Result = new JsonResult(new
                        {
                            Code = 403,
                            Message = "没有权限"
                        });
                    }
                }
                catch (SecurityTokenExpiredException stee)
                {
                    context.Result = new JsonResult(new
                    {
                        Code = 401,
                        stee.Message
                    });
                }
                catch (SecurityTokenException ste)
                {
                    context.Result = new JsonResult(new
                    {
                        Code = 402,
                        ste.Message,
                    });
                }
                catch (Exception e)
                {
                    context.Result = new JsonResult(new
                    {
                        Code = 405,
                        e.Message
                    });
                }
            }
        }
    }
}
